package com.clp.inflxudb.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.yaml.snakeyaml.Yaml;

import java.util.HashMap;
import java.util.Map;

public class FluxBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    private static final Logger LOGGER = LoggerFactory.getLogger(FluxBeanDefinitionRegistryPostProcessor.class);

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        // 扫描FluxMapper组件
        Map<String, Object> map = getMap();
        if (!map.containsKey("spring")) {
            return;
        }
        Map<String, Object> mapSpring = StringUtil.getMapValue(map, "spring", Map.class);
        if (!mapSpring.containsKey("influxdb")) {
            LOGGER.warn("未定义influxdb配置信息！");
            return;
        }

        Map mapSpringInfluxdb = StringUtil.getMapValue(mapSpring, "influxdb", Map.class);
        String mapperLocation = null;
        if (mapSpringInfluxdb.containsKey("mapper-locations")) {
            mapperLocation = StringUtil.getMapValue(mapSpringInfluxdb, "mapper-locations", String.class);
            int scan = new FluxClassPathBeanDefinitionScanner(beanDefinitionRegistry).scan(handleStr(mapperLocation, map));
        }

        // 添加自定义bean
        String urlStr, tokenStr;
        if (mapSpringInfluxdb.containsKey("url") && mapSpringInfluxdb.containsKey("token")) {
            urlStr = StringUtil.getMapValue(mapSpringInfluxdb, "url", String.class);
            tokenStr = StringUtil.getMapValue(mapSpringInfluxdb, "token", String.class);
            FluxTemplateFactoryBean.setFluxTemplate(handleStr(urlStr, map), handleStr(tokenStr, map));

            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FluxTemplateFactoryBean.class);
            // 注入到spring容器中
            beanDefinitionRegistry.registerBeanDefinition("FluxTemplate", builder.getBeanDefinition());
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }

    private Map<String, Object> getMap() {
        ClassLoader classLoader = this.getClass().getClassLoader();
        Iterable<Object> retMapIter = new Yaml().loadAll(classLoader.getResourceAsStream("application.yaml"));
        Map<String, Object> retMap = new HashMap<>();
        for (Object object : retMapIter) {
            retMap.putAll((Map<String, Object>) object);
        }
        if (retMap.containsKey("spring")) {
            Map mapSpring = StringUtil.getMapValue(retMap, "spring", Map.class);
            if (mapSpring.containsKey("profiles")) {
                Map mapSpringProfiles = StringUtil.getMapValue(mapSpring, "profiles", Map.class);
                if (mapSpringProfiles.containsKey("active")) {
                    String activeStr = StringUtil.getMapValue(mapSpringProfiles, "active", String.class);
                    Iterable<Object> applicationOtherMaps = new Yaml()
                            .loadAll(classLoader.getResourceAsStream("application-" + activeStr + ".yaml"));
                    for (Object otherMap : applicationOtherMaps) {
                        addMapAWithB(retMap, otherMap);
                    }
                }
            }
        }
        return retMap;
    }

    /**
     * 将多层嵌套map b 的所有值 放入 多层嵌套map a 中
     * @param a
     * @param b
     */
    private void addMapAWithB(Object a, Object b) {
        if (a instanceof Map && b instanceof Map) {
            Map<String, Object> aMap = (Map<String, Object>) a;
            Map<String, Object> bMap = (Map<String, Object>) b;
            for (Map.Entry<String, Object> entry : bMap.entrySet()) {
                if (!aMap.containsKey(entry.getKey())) {
                    aMap.put(entry.getKey(), entry.getValue());
                } else {
                    addMapAWithB(aMap.get(entry.getKey()), bMap.get(entry.getKey()));
                }
            }
        } else if (a instanceof Map) {
            // 不存在同名时的值既是map又是具体值
            return;
        } else if (b instanceof Map) {
            // 同上
            return;
        } else {
            // 如果同名键的值都是具体值，则抛出异常
            throw new RuntimeException("重复定义了值： " + a + " ！");
        }
    }

    /**
     * 处理字符串中的占位符
     * @param str
     * @param map
     * @return
     */
    private String handleStr(String str, Map<String, Object> map) {
        int count = 0;
        while (StringUtil.strContainsChar(str, '$')) {
            str = StringUtil.handlerPlaceHolder(str, '$', map);
            count++;
            if (count == 5) throw new RuntimeException("嵌套层数过深！");
        }
        return str;
    }
}
